#include <iostream>
#include <memory>
#include <vector>
#include <queue>

enum Size {
    Single_Size = 8,
    Max_Size = 128,
    Min_Size = 0
};

template <typename T>
class MemoryPool {
public:
    MemoryPool(size_t size) : poolSize(size) {
        for (size_t i = 0; i < size; i++) {
            pool.push(std::make_shared<T>());
        }
    }

    std::shared_ptr<T> acquire() {
        if (pool.empty()) {
            return std::make_shared<T>();
        }
        auto ptr = pool.front();
        pool.pop();
        return std::shared_ptr<T>(ptr.get(), [this](T* p) {
            pool.push(std::shared_ptr<T>(p));
        });
    }

private:
    size_t poolSize;
    std::queue<std::shared_ptr<T>> pool;
};

template <typename T>
class PoolAllocator {
public:
    using value_type = T;
    using pointer = std::shared_ptr<T>;
    using const_pointer = std::shared_ptr<const T>;
    using reference = T&;
    using const_reference = const T&;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;

    template <typename U>
    struct rebind {
        using other = PoolAllocator<U>;
    };

    PoolAllocator(MemoryPool<T>& pool) : memoryPool(pool) {}

    template <typename U>
    PoolAllocator(const PoolAllocator<U>& other) : memoryPool(*reinterpret_cast<MemoryPool<T>*>(&other.memoryPool)) {}

    pointer allocate(std::size_t n) {
        if (n > 1) {
            throw std::bad_alloc();
        }
        return memoryPool.acquire();
    }

    void deallocate(pointer p, std::size_t n) {
    }

    template <typename... Args>
    void construct(pointer p, Args&&... args) {
        new(p.get()) T(std::forward<Args>(args)...);
    }

    void destroy(pointer p) {
        p->~T();
    }

private:
    MemoryPool<T>& memoryPool;
};

int main() {
    MemoryPool<int> intPool(Max_Size);
    PoolAllocator<int> allocator(intPool);

    std::vector<std::shared_ptr<int>, PoolAllocator<std::shared_ptr<int>>> vec(allocator);
    vec.reserve(Max_Size);

    for (int i = 0; i < 10; i++) {
        auto ptr = allocator.allocate(1);
        allocator.construct(ptr, i);
        vec.push_back(ptr);
    }

    for (const auto& ptr : vec) {
        std::cout << *ptr << " ";
    }
    std::cout << std::endl;

    return 0;
}    


//实现了一个 MemoryPool 类，它是一个通用的内存池，使用 std::queue 来管理预分配的 std::shared_ptr 对象。
//PoolAllocator 类是一个自定义的分配器，它使用 MemoryPool 来分配和释放内存，并且可以与标准容器（如 std::vector）一起使用。
// #include <memory>
// #include <iostream>

// using namespace std;


// template <bool threads, int inst>
// class __default_alloc_template
// {
// public:
//     static void * allocate( size_t n );
//     static void deallocate( void *p, size_t n );

// };


// template <bool threads, int inst>
// void* __default_alloc_template<threads, inst>::allocate( size_t n ) 
// {
//     cout << "模拟申请内存，需要转化成最终的实现\n";
//     return malloc( n );     // 此处只是模拟
// }

// template <bool threads, int inst>
// void __default_alloc_template<threads, inst>::deallocate( void *p, size_t n )
// {
//     cout << "模拟像内存池归还\n"; 
//     return free( p );
// }

// typedef __default_alloc_template<false, 0> single_client_alloc;

// /// __default_alloc_template类的适配器
// //
// //  由于__default_alloc_template的内存池归还函数是两个参数，而shared_ptr在不是释放数组的时候，deleter只能
// //  接收一个参数，所以需要写一个适配器，在构造函数的时候，把大小先保留好
// //
// template <typename T>
// class AdaptDefaultAlloTemplate
// {
// public:
//     AdaptDefaultAlloTemplate(int size) : _size( size ) { }
//     void operator() (T* p) {
//         single_client_alloc::deallocate( p, _size );
//     }
// private:
//     int _size;
// };

// template <typename T>
// class CMemoryPool 
// {
// public:
//     static shared_ptr<T> make_shared_with_pool();
// };


// template <typename T>
// shared_ptr<T> CMemoryPool<T>::make_shared_with_pool() 
// {
//     shared_ptr<T> ptr( static_cast<T*>(single_client_alloc::allocate( sizeof( T ) ) ),
//                        AdaptDefaultAlloTemplate<T>( sizeof( T ) ) );
//     return ptr;
// }

// int main()
// {

//     shared_ptr<int> ptr = CMemoryPool<int>::make_shared_with_pool();

//     *ptr = 100;

//     cout << *ptr << endl;

//     return 0;
// }